package io.adrainty.gbatis.core;

import io.adrainty.gbatis.entity.MapperStatement;
import io.adrainty.gbatis.exception.CoreException;
import io.adrainty.gbatis.util.ReflectUtil;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;

/**
 * 执行SQL语句的会话对象
 * @author adrainty
 * @version v1.0.0
 * @since 2024/1/17 11:29
 */

@SuppressWarnings("unused")
public class SqlSession {

    private final SqlSessionFactory sqlSessionFactory;

    private static final String PATTERN = "#\\{\\w*}";

    public SqlSession(SqlSessionFactory sqlSessionFactory) {
        this.sqlSessionFactory = sqlSessionFactory;
    }

    /**
     * 向数据库插入记录
     * @param statement SQL语句的ID
     * @param o 插入的数据
     * @return 数据库更新的行
     */
    public int insert(String statement, Object o) throws SQLException {
        Connection connection = sqlSessionFactory.getTransaction().getConnection();
        MapperStatement mapperStatement = sqlSessionFactory.getMapperStatements().get(statement);
        if (mapperStatement == null) {
            throw new CoreException(String.format("Binding Error, Cannot find [%s]", statement));
        }
        String sql = mapperStatement.getSql();
        String executeSQL = sql.replaceAll(PATTERN, "?");
        try (
                PreparedStatement preparedStatement = connection.prepareStatement(executeSQL)
        ) {
            setParameter(preparedStatement, sql, o);
            return preparedStatement.executeUpdate();
        }
    }

    /**
     * 查询单条记录
     * @param statement SQL语句的ID
     * @param o 条件, JavaBean对象
     * @return 查询的结果
     */
    public Object selectOne(String statement, Object o) throws SQLException {
        Connection connection = sqlSessionFactory.getTransaction().getConnection();
        MapperStatement mapperStatement = sqlSessionFactory.getMapperStatements().get(statement);
        if (mapperStatement == null) {
            throw new CoreException(String.format("Binding Error, Cannot find [%s]", statement));
        }
        String sql = mapperStatement.getSql();
        String resultType = mapperStatement.getResultType();
        String executeSQL = sql.replaceAll(PATTERN, "?");

        try (
                PreparedStatement preparedStatement = connection.prepareStatement(executeSQL)
        ) {
            setParameter(preparedStatement, sql, o);
            Class<?> aClass = Class.forName(resultType);
            Object instance = aClass.getConstructor().newInstance();
            ResultSet resultSet = preparedStatement.executeQuery();     // 执行查询语句
            if (!resultSet.next()) return null;
            // 反射赋值
            List<String> fieldNames = Arrays.stream(aClass.getDeclaredFields()).map(Field::getName).toList();
            // 另外一种方法是resultSet.getMetaData()后rm.getColumnCount()来循环
            for (String fieldName : fieldNames) {
                String fieldValue = resultSet.getString(fieldName);
                if (fieldValue != null) {
                    ReflectUtil.setFieldValue(instance, fieldName, fieldValue);
                }
            }
            return instance;
        } catch (SQLException e) {
            throw e;
        } catch (Exception e) {
            throw new CoreException(e.getMessage());
        }
    }

    /**
     * 提交事物
     * @throws SQLException SQL异常
     */
    public void commit() throws SQLException {
        sqlSessionFactory.getTransaction().commit();
    }

    /**
     * 回滚事物
     * @throws SQLException SQL异常
     */
    public void rollback() throws SQLException {
        sqlSessionFactory.getTransaction().rollback();
    }

    /**
     * 关闭事物
     * @throws SQLException SQL异常
     */
    public void close() throws SQLException {
        sqlSessionFactory.getTransaction().close();
    }

    /**
     * 属性赋值
     * 给#{xxx}赋值
     * @param preparedStatement 绑定的语句
     * @param sql SQL语句
     * @param o 入参
     */
    private void setParameter(PreparedStatement preparedStatement, String sql, Object o) throws SQLException {
        int paramIndex = 1;
        StringBuilder curParam = new StringBuilder();
        boolean isParam = false;
        char pre = 0;
        for (char c : sql.toCharArray()) {
            if (c == '{' && pre == '#') {
                isParam = true;
            } else if (c == '}' && isParam) {
                isParam = false;
                String param = curParam.toString();
                String objData = ReflectUtil.getFieldValue(o, param);
                preparedStatement.setString(paramIndex++, objData);     // 实际赋值
                curParam.setLength(0);      // 清空
            } else if (isParam) {
                curParam.append(c);
            }
            pre = c;
        }
    }



}
